﻿using Microsoft.AspNetCore.Mvc.RazorPages;

namespace HHFileViewServer.Pages
{
    public class PreviewModel : PageModel
    {
        private readonly ILogger<PreviewModel> _logger;
        private readonly HttpClient _httpClient;
        private readonly IServiceProvider _serviceProvider;

        public PreviewModel(
            ILogger<PreviewModel> logger,
            HttpClient httpClient,
            IServiceProvider serviceProvider)
        {
            _logger = logger;
            _httpClient = httpClient;
            _serviceProvider = serviceProvider;
        }

        // Host
        public string Host => Request.Scheme + "://" + Request.Host.Value;

        public async Task OnGet()
        {
            string url = string.Empty;
            try
            {
                var qs = this.HandleQueryString();
                url = qs.url;
                if (string.IsNullOrEmpty(qs.password))
                {
                    ViewData["model"] = Result.Success(await this.PreviewFile(qs.url));
                }
                else
                {
                    ViewData["model"] = Result.Success(await this.PreviewFile(qs.url, qs.password));
                }
            }
            catch (SharpCompress.Common.CryptographicException ex)
            {
                // 压缩包需要密码
                ViewData["model"] = Result.Success(
                    (ex.ExceptionType == 1 ? "" : ex.Message),
                    new BaseFileInfo()
                    {
                        FileType = FILETYPE.PASSWORD,
                        Path = url,
                    });
            }
            catch (CustomException ex)
            {
                ViewData["model"] = Result.Fail(ex.Message);
            }
            catch (Exception ex)
            {
                _logger.LogError("文件预览失败：", ex);
                ViewData["model"] = Result.Fail("文件预览失败");
            }
        }

        /// <summary>
        /// 预览文件
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<BaseFileInfo> PreviewFile(string url)
        {
            var local = FileHelper.IsLocalFile(Host, url);
            BaseFileInfo baseFileInfo = local.isLocal ?
                // 本地文件
                FileHelper.GetLocalFile(local.path) :
                // 远程下载
                await this.DownloadFile(url);

            switch (baseFileInfo.FileType)
            {
                case FILETYPE.OFFICE:
                    baseFileInfo = _serviceProvider.GetService<IService, OfficeService>().Conversion(baseFileInfo);
                    break;
                case FILETYPE.COMPRESS:
                    baseFileInfo = _serviceProvider.GetService<IService, CompressionService>().Conversion(baseFileInfo);
                    break;
            }
            if (!string.IsNullOrEmpty(baseFileInfo.Path))
                baseFileInfo.Path = FileHelper.HandlePathByLibreOffice(baseFileInfo.Path);
            if (!string.IsNullOrEmpty(baseFileInfo.AbsolutePath))
                baseFileInfo.AbsolutePath = FileHelper.HandlePathByLibreOffice(baseFileInfo.AbsolutePath);
            return baseFileInfo;
        }

        /// <summary>
        /// 预览压缩包（带密码）
        /// </summary>
        /// <param name="url"></param>
        /// <param name="password">压缩包解压密码</param>
        /// <returns></returns>
        public async Task<BaseFileInfo> PreviewFile(string url, string password)
        {
            var local = FileHelper.IsLocalFile(Host, url);
            BaseFileInfo baseFileInfo = local.isLocal ?
                // 本地文件
                FileHelper.GetLocalFile(local.path) :
                // 远程下载
                await this.DownloadFile(url);

            // 解压缩
            baseFileInfo = _serviceProvider.GetService<IService, CompressionService>().Conversion(baseFileInfo, password);

            baseFileInfo.Path = FileHelper.HandlePathByLibreOffice(baseFileInfo.Path);
            baseFileInfo.AbsolutePath = FileHelper.HandlePathByLibreOffice(baseFileInfo.AbsolutePath);
            return baseFileInfo;
        }

        /// <summary>
        /// 下载文件并保存到指定目录
        /// </summary>
        /// <param name="url">文件下载地址</param>
        /// <returns>下载是否成功</returns>
        public async Task<BaseFileInfo> DownloadFile(string url)
        {
            try
            {
                // 文件名
                string fileName = FileHelper.GetFileName(url).Replace(" ", "");
                // 文件扩展名
                string extension = Path.GetExtension(fileName);
                // 文件请求
                HttpResponseMessage response = await _httpClient.GetAsync(url);
                if (!response.IsSuccessStatusCode)
                {
                    // 下载失败
                    throw new CustomException("文件下载失败，状态码：" + response.StatusCode);
                }
                if (string.IsNullOrWhiteSpace(extension))
                {
                    // 重新获取文件名
                    this.GetFileName(response, ref fileName);
                    // 重新获取扩展名
                    extension = Path.GetExtension(fileName);
                }
                BaseFileInfo baseFileInfo = new BaseFileInfo();
                using (Stream stream = await response.Content.ReadAsStreamAsync())
                {
                    if (stream.Length > Const.FileSizeLimit)
                    {
                        double realSize = Math.Round(stream.Length / (double)1024, 1);
                        throw new CustomException($"文件大小({realSize}KB)超出上限：{ServerOp.ServerConfig.SizeLimit}KB");
                    }
                    // 文件流转字节数组
                    var streamByte = await stream.StreamToByte();
                    if (string.IsNullOrEmpty(extension))
                    {
                        // 没有扩展名（根据文件魔数判断-不一定准确）
                        var fileInfo = FileHelper.JudgeFileType(streamByte.bytes);
                        if (string.IsNullOrEmpty(fileInfo.extension))
                        {
                            throw new CustomException("未识别到的文件类型！");
                        }
                        // 更新扩展名
                        extension = fileInfo.extension;
                        // 更新文件名
                        fileName += extension;
                        baseFileInfo.FileType = fileInfo.fileType;
                    }
                    else
                    {
                        // 有扩展名
                        baseFileInfo.FileType = FileHelper.JudgeFileType(extension).fileType;
                    }
                    // 文件的绝对地址
                    baseFileInfo.AbsolutePath = Const.AbsolutePath(fileName);
                    // 写入到本地文件
                    using (FileStream fileStream = new FileStream(baseFileInfo.AbsolutePath, FileMode.OpenOrCreate))
                    {
                        fileStream.Write(streamByte.bytes, 0, streamByte.length);
                    }

                    // 更新文件信息
                    baseFileInfo.Name = fileName;
                    baseFileInfo.Extension = extension;
                    baseFileInfo.Path = Const.RelativePath(fileName);
                }
                return baseFileInfo;
            }
            catch (IOException ex)
            {
                _logger.LogError("下载文件IO异常：", ex);
                throw new CustomException("文件下载出错，请查看相关日志文件！");
            }
            catch (Exception ex)
            {
                _logger.LogError("下载文件异常：", ex);
                throw new CustomException("下载文件异常" + (ex.InnerException != null ? ex.InnerException.Message : ex.Message));
            }
        }

        /// <summary>
        /// 解码下载地址
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private string decodeUrl(string url)
        {
            url = System.Web.HttpUtility.UrlDecode(url, System.Text.Encoding.UTF8);
            try
            {
                byte[] _byte = Convert.FromBase64String(url);
                url = System.Text.Encoding.UTF8.GetString(_byte);
            }
            catch (System.FormatException ex)
            {
                _logger.LogWarning($"Url：{url}Base64解码失败！", ex.ToString());
                return url;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Url：{url}Base64解码失败！", ex.ToString());
            }
            return url;
        }

        /// <summary>
        /// 获取文件名（针对cloudreve）
        /// </summary>
        /// <param name="httpResponse"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private void GetFileName(HttpResponseMessage httpResponse, ref string fileName)
        {
            var disposition = httpResponse.Content.Headers.ContentDisposition;
            if (disposition != null)
                fileName = System.Web.HttpUtility
                    .UrlDecode(disposition.FileName.ToString(), System.Text.Encoding.UTF8)
                    .Replace(@"\", "")
                    .Replace("\"", "")
                    .Replace(" ", ""); ;
        }

        /// <summary>
        /// 处理请求数据
        /// </summary>
        /// <returns></returns>
        private (string url, string password) HandleQueryString()
        {
            var query = Request.Query;
            if (query == null || !query.Keys.Any() || !query.Keys.Any(x => x == "url"))
            {
                throw new CustomException("参数错误，预览失败！");
            }
            string url = query["url"];
            if (string.IsNullOrEmpty(url))
            {
                throw new CustomException("请传入下载地址！");
            }
            // 先解密
            url = this.decodeUrl(url);
            if (!url.ToLower().StartsWith("http"))
            {
                throw new CustomException("请传入合法下载地址！");
            }
            string password = string.Empty;
            if (query.Keys.Any(x => x == "password"))
            {
                password = query["password"];
            }
            return (url, password);
        }

    }
}